// Effective C++ 条款28：避免返回 handles 指向对象内部成分

// # 核心概念
// 条款28的核心思想是：避免返回指向对象内部数据的引用、指针或迭代器（统称为"handles"），因为这会降低封装性，可能导致对象内部状态被意外修改，并可能造成悬空引用问题。

// # 详细解释
// 1. 什么是"handles"？
// 在这个条款中，"handles"指的是能够访问对象内部的方式，主要包括：

// 引用（references）
// 指针（pointers）
// 迭代器（iterators）

// 2. 为什么要避免返回指向内部成分的handles？
// 问题一：破坏封装
class Rectangle
{
private:
    Point topLeft;
    Point bottomRight;

public:
    // 不好的设计：返回内部成分的引用
    Point &upperLeft() { return topLeft; }
    Point &lowerRight() { return bottomRight; }
};
// 这种设计允许客户代码直接修改对象的内部状态：
Rectangle rec;
// 直接修改了rec的内部状态
rec.upperLeft().setX(50);

// 问题二：可能导致悬空引用（dangling handles）
class StringHolder
{
private:
    std::string text;

public:
    // 危险：返回内部成分的引用
    const std::string &getText() const { return text; }
};

const std::string &dangerousFunc()
{
    StringHolder h;
    return h.getText(); // 返回了临时对象内部成分的引用！
} // h被销毁，引用悬空
// 当函数返回后，临时对象h被销毁，但返回的引用仍指向已销毁对象的内部成分，导致悬空引用。

// 问题三：意外违反类的不变性（invariants）
class Date
{
private:
    int day, month, year;
    // 确保日期有效的内部函数
    bool isValid() const;

public:
    // 危险：返回内部成分的引用
    int &getDay() { return day; }

    // 设置日期时会检查有效性
    void setDate(int d, int m, int y)
    {
        // 验证日期有效性...
        day = d;
        month = m;
        year = y;
    }
};
// 客户可以绕过有效性检查：
Date d;
d.setDate(15, 3, 2023); // 正常设置
int &dayRef = d.getDay();
dayRef = 32; // 直接修改为无效值，绕过了验证

// 3. 解决方案
// 方案一：返回对象的副本而非引用
class Rectangle
{
private:
    Point topLeft;
    Point bottomRight;

public:
    // 更好的设计：返回副本
    Point upperLeft() const { return topLeft; }
    Point lowerRight() const { return bottomRight; }
};

// 方案二：提供只读访问（const引用）
// 如果担心性能问题且确实需要返回引用，可以返回const引用：
class BigObject
{
private:
    std::vector<int> data;

public:
    // 返回const引用，防止修改
    const std::vector<int> &getData() const { return data; }
};
// 但这仍然存在封装性问题，客户代码可以看到内部实现细节。

// 方案三：使用访问器和修改器函数
class Date
{
private:
    int day, month, year;
    bool isValid() const;

public:
    // 访问器
    int getDay() const { return day; }

    // 修改器（带验证）
    void setDay(int d)
    {
        // 验证日期有效性...
        day = d;
    }
};

// 总结
// 1.避免返回指向对象内部的非const handles（引用、指针、迭代器）
// 2.优先返回对象的副本，尤其是对于小型对象
// 3.如果必须返回handles，考虑返回const handles，减少被修改的风险
// 4.提供明确的访问器和修改器函数，而不是直接暴露内部数据
// 5.记住封装的价值：它允许你更改实现而不影响客户代码
// 遵循这一条款有助于提高代码的封装性、安全性和可维护性，减少因意外修改对象内部状态而导致的错误。